package com.lancoo.edu.platform.operation.commons.task;

import com.lancoo.edu.platform.operation.commons.exception.ServiceException;
import com.lancoo.edu.platform.operation.commons.task.enums.IntervalUnitEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 定时任务调度器
 *
 * @author pky
 */
@Slf4j
@Component
@AllArgsConstructor
public class TaskScheduler {

    private final Scheduler scheduler;

    /**
     * 启动调度器
     *
     * @param jobDetail 任务
     * @param trigger   任务触发器
     */
    public void startScheduler(JobDetail jobDetail, Trigger trigger) {
        String name = jobDetail.getKey().getName();
        String group = jobDetail.getKey().getGroup();
        try {
            JobDetail schedulerJobDetail = scheduler.getJobDetail(new JobKey(name, group));
            scheduler.scheduleJob(jobDetail, trigger);
            if (schedulerJobDetail == null) {
                scheduler.start();
            }
            log.info("【启动调度器成功：{}】", group);
        } catch (Exception e) {
            log.error("【启动调度器失败：{}】", group, e);
            throw new ServiceException("启动调度器失败");
        }
    }

    /**
     * 获取任务
     *
     * @param jobName  任务名
     * @param jobGroup 任务所在组名
     * @param cla      任务实体
     * @param <T>      继承 Job 的任务实体
     * @return 任务
     */
    public <T extends Job> JobDetail getJob(String jobName, String jobGroup, Class<T> cla) {
        try {
            JobDetail jobDetail = scheduler.getJobDetail(new JobKey(jobName, jobGroup));
            if (jobDetail != null) {
                return jobDetail;
            }
            return JobBuilder.newJob(cla).withIdentity(jobName, jobGroup).build();
        } catch (Exception e) {
            log.error("【获取任务失败：{}】", jobGroup, e);
            throw new ServiceException("获取任务失败");
        }
    }

    /**
     * 创建 cron 触发器
     *
     * @param triggerName  触发器名
     * @param triggerGroup 触发器所在组名
     * @param startTime    触发器开始时间
     * @param endTime      触发器关闭时间
     * @param cron         cron 表达式
     * @return 触发器
     */
    public Trigger buildCronTrigger(String triggerName, String triggerGroup, Date startTime, Date endTime, String cron) {
        // 触发器
        return TriggerBuilder.newTrigger()
                .withIdentity(triggerName, triggerGroup)
                // 开始日期
                .startAt(startTime)
                // 关闭日期
                .endAt(endTime)
                // 触发的具体时间 cron 表达式，并且项目启动时不触发，到cron时间才触发
                .withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing())
                .build();
    }

    /**
     * 创建 interval 触发器
     *
     * @param triggerName  触发器名
     * @param triggerGroup 触发器所在组名
     * @param startTime    触发器开始时间
     * @param endTime      触发器关闭时间
     * @param interval     间隔频率
     * @param intervalType 间隔类型
     * @return 触发器
     */
    public Trigger buildIntervalTrigger(String triggerName, String triggerGroup, Date startTime, Date endTime, Integer interval, Integer intervalType) {
        IntervalUnitEnum intervalUnitEnum = IntervalUnitEnum.getIntervalUnitEnum(intervalType);
        // 触发器
        return TriggerBuilder.newTrigger()
                .withIdentity(triggerName, triggerGroup)
                // 开始日期
                .startAt(startTime)
                // 关闭日期
                .endAt(endTime)
                // 触发的频率 和 频率单位。
                .withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withInterval(interval, intervalUnitEnum.getIntervalUnit())
                        .withMisfireHandlingInstructionDoNothing())
                .build();
    }

    /**
     * 更新 cron 触发器
     *
     * @param triggerName  触发器名
     * @param triggerGroup 触发器组名
     * @param startTime    触发器开始时间
     * @param endTime      触发器关闭时间
     * @param cron         cron 表达式
     * @return true/成功，false/失败
     */
    public Boolean updatedCronTrigger(String triggerName, String triggerGroup, Date startTime, Date endTime, String cron) {
        // 1、获取旧的触发器
        TriggerKey oldTrigger = new TriggerKey(triggerName, triggerGroup);
        // 2、创建新的触发器
        Trigger newTrigger = buildCronTrigger(triggerName, triggerGroup, startTime, endTime, cron);
        // 3、替换触发器
        try {
            scheduler.rescheduleJob(oldTrigger, newTrigger);
            log.info("【更新 cron 触发器成功：{}】", triggerName);
            return true;
        } catch (Exception e) {
            log.error("【更新 cron 触发器失败：{}】", triggerName, e);
            throw new ServiceException("更新 cron 触发器失败");
        }
    }

    /**
     * 更新 cron 触发器
     *
     * @param triggerName  触发器名
     * @param triggerGroup 触发器组名
     * @param startTime    触发器开始时间
     * @param endTime      触发器关闭时间
     * @param interval     间隔频率
     * @param intervalType 间隔类型
     * @return true/成功，false/失败
     */
    public Boolean updatedIntervalTrigger(String triggerName, String triggerGroup, Date startTime, Date endTime, Integer interval, Integer intervalType) {
        // 1、获取旧的触发器
        TriggerKey oldTrigger = new TriggerKey(triggerName, triggerGroup);
        // 2、创建新的触发器
        Trigger newTrigger = buildIntervalTrigger(triggerName, triggerGroup, startTime, endTime, interval, intervalType);
        // 3、替换触发器
        try {
            scheduler.rescheduleJob(oldTrigger, newTrigger);
            log.info("【更新 interval 触发器成功：{}】", triggerName);
            return true;
        } catch (Exception e) {
            log.error("【更新 interval 触发器失败：{}】", triggerName, e);
            throw new ServiceException("更新 interval 触发器失败");
        }
    }

    /**
     * 删除触发器
     *
     * @param triggerName  触发器名
     * @param triggerGroup 触发器组名
     * @return true/成功，false/失败
     */
    public Boolean removeTrigger(String triggerName, String triggerGroup) {
        try {
            TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
            // 停止触发器
            scheduler.pauseTrigger(triggerKey);
            // 移除触发器
            boolean flag = scheduler.unscheduleJob(triggerKey);
            log.info("【移除触发器成功：{}】", triggerName);
            return flag;
        } catch (Exception e) {
            log.error("【移除触发器失败：{}】", triggerName, e);
            throw new ServiceException("移除触发器失败");
        }
    }
}
